#include "timer.hpp"

#include "debug.h"
#include <iostream>

int TimerNode::nodeCount = 0;
int TimerUnit::unitCount = 0;

void Timer::proc (void)
{
  struct timeval now;
  
  gettimeofday (&now, NULL);
  this->proc (&now);

  return ;
}

void Timer::proc (struct timeval * now)
{
  if (now->tv_sec <= this->tv.tv_sec)
    return ;

  if (this->tv.tv_sec == 0)
    {
      this->tv.tv_sec  = now->tv_sec;
      this->tv.tv_usec = now->tv_usec;
    }      

  for (unsigned int t = now->tv_sec - this->tv.tv_sec; t > 0; t--)
    {
      TimerUnit * u;
      TimerNode * node;
      // debug (DEBUG, "tick, tack... t = %d", this->tick);
           
      // unsigned int tp = this->tick % this->size;      
      // unsigned int up = this->tick ;      
      // if (NULL != (u = this->unit[tp].lookup (&up, sizeof (unsigned int))))

      debug (DEBUG, "Check proc (tick = %u)", this->tick);
      if (NULL != (u = this->lookupTimerUnit (this->tick)))
	{
	  debug (DEBUG, "Start proc (tick = %u)", this->tick);
	  for (node = u->root.next ; node != &(u->root); node = node->next)
	    {
	      node->proc ();	      
	    }

	  u->releaseNode ();
	  this->removeTimerUnit (this->tick);
	  debug (DEBUG, "End proc (tick = %u)", this->tick);
	}
      
      this->tick++;
    }
  
  memcpy (&this->tv, now, sizeof (struct timeval));  
  return ;
}

void Timer::attachTimerNode (TimerNode * tnode, unsigned int tick)
{
  TimerUnit * u = this->fetchTimerUnit (tick);
  tnode->procTick = tick;
  tnode->nextTick = 0;
  tnode->reset = false;
  u->root.attach (tnode);    
}


TimerNode * Timer::setTimer (void (*func)(void *), void * obj, int at)
{
  // debug (DEBUG, "set timer (func)%p (%p) to %u", func, obj, tick);
  TimerNode * tnode = new TimerNode ();
  tnode->setProc (func, obj);
  this->attachTimerNode (tnode, this->tick + at);
  return tnode;
}

void Timer::resetTimer (TimerNode * tnode, int at)
{
  // assert (tnode->procTick <= this->tick + at);
  assert (at > 0);
  unsigned int up = (this->tick + at) ; // Unit Point
  if (up > tnode->nextTick && up > tnode->procTick)
    {
      tnode->nextTick = up;
      tnode->reset = true;
    }

  // TimerUnit * u = this->unit[tp].fetch (&up, sizeof (unsigned int));
  // u->root.attach (tnode); 
  return ;
}

void TimerUnit::releaseNode ()
{
  TimerNode * tnode, * next;

  debug (DEBUG, "Start release");
  for (tnode = this->root.next; tnode != &(this->root); tnode = next)
    {
      next = tnode->next;
      tnode->detach ();

      if (tnode->active && tnode->reset)
	{
	  tnode->done  = false;
	  tnode->reset = false;	  
	  this->timer->attachTimerNode (tnode, tnode->nextTick);
	}
      else
	{
	  delete tnode;
	}
    }

  debug (DEBUG, "End release");
}

TimerUnit * Timer::fetchTimerUnit (unsigned int tick)
{
  unsigned int tp = tick % this->size; // Tick Point
  unsigned int up = tick; // Tick Point
  TimerUnit * u = this->unit[tp].fetch (&up, sizeof (unsigned int));
  u->timer = this;
  return u;
}

TimerUnit * Timer::lookupTimerUnit (unsigned int tick)
{
  unsigned int tp = tick % this->size; // Tick Point
  unsigned int up = tick; // Tick Point
  TimerUnit * u = this->unit[tp].lookup (&up, sizeof (unsigned int));
  return u;
}

void Timer::removeTimerUnit (unsigned int tick)
{
  unsigned int tp = tick % this->size; // Tick Point
  unsigned int up = tick; // Tick Point
  this->unit[tp].remove (&up, sizeof (unsigned int));
}

/******************************************************
 * TimerNode Functions
 ******************************************************/

void * TimerNode::operator new (size_t size)
{
  // if (size != sizeof (TimerNode))
  return ::operator new (size);
}

void TimerNode::operator delete (void * obj, size_t size)
{
  if (NULL == obj)
    return ;

  ::operator delete (obj);
}


void TimerNode::setProc (void (*func)(void *), void * obj)
{
  this->func   = func;
  this->obj    = obj;
  this->active = true;
  this->done   = false;
}

void TimerNode::enable ()
{
  this->active = true;
}

void TimerNode::disable ()
{ 
  this->active = false;
}


void TimerNode::proc ()
{
  assert (this->done == false);

  if (this->active &&
      (!this->reset || (this->reset && this->procTick >= this->nextTick)))
    {
      // debug (DEBUG, "proc: %p (%p)", this->func, this->obj);
      this->reset = false;
      this->done  = true;
      this->func (obj);
    }
}

void TimerNode::attach (TimerNode * node)
{
  TimerNode * next = this->next;
  node->next = next;
  node->prev = this;
  this->next = node;
  next->prev = node;
}

void TimerNode::detach ()
{
  TimerNode * next, * prev;
  next = this->next;
  prev = this->prev;
  next->prev = prev;
  prev->next = next;
  this->next = this->prev = NULL;
}
